import { updateNodeElement } from '../DOM'
import { createTaskQueue, arrified, createStateNode, getTag } from '../Misc'
const taskQueue = createTaskQueue()
let subTask = null
let pendingCommit = null

const commitAllWork = fiber => {
  console.log('commitAllWork effects', fiber.effects)
  /**
   * 循环 effects 数组， 构建 DOM 节点树
   */
  fiber.effects.forEach(item => {
    if (item.effectsTag === 'delete') {
      item.parent.stateNode.removeChild(item.stateNode)
    } else if (item.effectsTag === 'update') {
      /**
       * 更新
       */
      if (item.type === item.alternate.type) {
        /**
         * 节点类型相同
         */
        updateNodeElement(item.stateNode, item, item.alternate)
      } else {
        /**
         * 节点类型不同
         */
        item.parent.stateNode.replaceChild(
          item.stateNode,
          item.alternate.stateNode
        )
      }
    } else if (item.effectsTag === 'placement') {
      /**
       * 当前要追加的子节点
       */
      let fiber = item
      /**
       * 当前要追加的子节点的父级
       */
      let parentFiber = item.parent
      /**
       * 找到普通节点父级，排除组件父级
       * 因为组件父级是不能追加真实 DOM 节点的
       */
      while (
        parentFiber.tag === 'class_component' ||
        parentFiber.tag === 'function_component'
      ) {
        // 继续向上查找父节点直到父节点是普通节点
        parentFiber = parentFiber.parent
      }
      if (fiber.tag === 'host_component') {
        parentFiber.stateNode.appendChild(fiber.stateNode)
      }
    }
  })
  /**
   * 备份旧的 fiber 节点对象（也就是根节点对应的 fiber 对象）
   */
  fiber.stateNode.__rootFiberContainer = fiber
}
/**
 *  获取第一个子任务（任务队列中的任务的第一个小任务）
 */
const getFirstTask = () => {
  /**
   * 从任务队列中获取任务
   */
  const task = taskQueue.pop()
  /**
   * 返回最外层节点的 fiber 对象
   */
  return {
    // 节点属性
    props: task.props,
    // 节点 DOM 对象
    stateNode: task.dom,
    // 节点标记 hostRoot 表示根节点
    tag: 'host_root',
    // 需要更改的 fiber 对象
    effects: [],
    // 当前 fiber 的子级 fiber
    child: null,
    // 旧的根节点的备份 fiber 对象
    alternate: task.dom.__rootFiberContainer
  }
}
const reconcileChildren = (fiber, children) => {
  console.log('reconcileChildren', fiber)
  /**
   * childern 可能是对象也可能是数组
   * 将 childern 转换成数组
   */
  const arrifiedChildren = arrified(children)

  let index = 0
  /**
   * childern 数组中元素的个数
   */
  let numberOfElements = arrifiedChildren.length
  /**
   * 循环过程中的循环项，就是子节点的 vdom 对象
   */
  let element = null
  /**
   * 子级 fiber 对象
   */
  let newFiber = null
  /**
   * 上一个兄弟 fiber 对象
   */
  let prevFiber = null
  let alternate = null
  if (fiber.alternate && fiber.alternate.child) {
    // fiber.alternate.child 是 childern 数组中第一个子节点对应的备份节点
    alternate = fiber.alternate.child
  }
  while (index < numberOfElements || alternate) {
    element = arrifiedChildren[index]
    if (!element && alternate) {
      console.log('删除')
      alternate.effectsTag = 'delete'
      fiber.effects.push(alternate)
    } else if (element && alternate) {
      console.log('更新')
      newFiber = {
        type: element.type,
        props: element.props,
        // host_component 表示普通节点
        tag: getTag(element),
        effects: [],
        effectsTag: 'update',
        stateNode: null,
        // 当前节点的父级 fiber
        parent: fiber,
        alternate
      }
      console.log('更新newFiber', newFiber)
      if (element.type === alternate.type) {
        /**
         * 类型相同
         */
        newFiber.stateNode = alternate.stateNode
      } else {
        /**
         * 类型不同，不需要进行比对
         * 为 fiber 节点添加 DOM 对象或组件实例对象
         */
        newFiber.stateNode = createStateNode(newFiber)
      }
    } else if (element && !alternate) {
      console.log('初始渲染')
      /**
       * 初始渲染
       */
      newFiber = {
        type: element.type,
        props: element.props,
        // host_component 表示普通节点
        tag: getTag(element),
        effects: [],
        effectsTag: 'placement',
        stateNode: null,
        // 当前节点的父级 fiber
        parent: fiber
      }
      console.log('初始渲染newFiber', newFiber)
      /**
       * 为 fiber 节点添加 DOM 对象或组件实例对象
       */
      newFiber.stateNode = createStateNode(newFiber)
    }

    // 为父级 fiber 添加子级 fiber
    if (index === 0) {
      // 第一个子节点才是父级 fiber 的子节点
      fiber.child = newFiber
    } else if (element) {
      // 为 fiber 添加下一个兄弟 fiber
      // 其余子节点是上一个子节点的兄弟节点
      prevFiber.sibling = newFiber
    }

    if (alternate && alternate.sibling) {
      alternate = alternate.sibling
    } else {
      alternate = null
    }
    // 存储上一个子节点，下一次循环时为其添加兄弟节点
    prevFiber = newFiber
    index++
  }
}
const executeTask = fiber => {
  console.log('executeTask-before', fiber)
  /**
   * 构建子级 fiber 对象
   */
  if (fiber.tag === 'class_component') {
    // 类组件，子元素是调用组件实例对象的render方法的返回值
    console.log('类组件的子元素', fiber, fiber.stateNode.render())
    reconcileChildren(fiber, fiber.stateNode.render())
  } else if (fiber.tag === 'function_component') {
    // 函数组件，子元素是调用组件的type属性存储的函数
    reconcileChildren(fiber, fiber.stateNode(fiber.props))
  } else {
    reconcileChildren(fiber, fiber.props.children)
  }
  console.log('executeTask-after', fiber)
  /**
   *  如果子级存在，返回子级
   *  将这个子级当做父级，构建这个父级下的子级
   */
  if (fiber.child) {
    return fiber.child
  }
  /**
   * 如果存在同级，返回同级，构建同级的子级
   * 如果同级不存在，返回到父级，看父级是否有同级
   */
  let currentExcutelyFiber = fiber
  console.log('xxx', fiber)
  while (currentExcutelyFiber.parent) {
    currentExcutelyFiber.parent.effects =
      currentExcutelyFiber.parent.effects.concat(
        currentExcutelyFiber.effects.concat([currentExcutelyFiber])
      )
    if (currentExcutelyFiber.sibling) {
      return currentExcutelyFiber.sibling
    }
    currentExcutelyFiber = currentExcutelyFiber.parent
  }
  console.log('executeTask-final', fiber)
  pendingCommit = currentExcutelyFiber
  console.log('pendingCommit', pendingCommit)
}
const workloop = deadline => {
  if (!subTask) {
    /**
     * 如果子任务不存在，就去获取子任务
     */
    subTask = getFirstTask()
  }
  while (subTask && deadline.timeRemaining() > 1) {
    /**
     * 如果任务存在并且浏览器有空闲时间就调用 executeTask 执行任务
     * executeTask 方法执行任务，接收任务 返回新的任务
     */
    subTask = executeTask(subTask)
  }
  if (pendingCommit) {
    commitAllWork(pendingCommit)
  }
}
const performtask = deadline => {
  /**
   * 执行任务
   */
  workloop(deadline)
  /**
   * 代码走到这里，说明当前任务被其他高优先级任务打断了，需要重新注册任务
   * 判断任务是否存在
   * 判断任务队列中是否还有任务没有执行
   * 再一次告诉浏览器在空闲时执行任务
   */
  if (subTask && !taskQueue.isEmpity()) {
    requestIdleCallback(performtask)
  }
}
export const render = (element, dom) => {
  // element 要渲染元素的 vdom 对象
  console.log('render', element, dom)
  /**
   * 1. 向任务队列中添加任务
   * 2. 指定在浏览器空闲时执行任务
   */

  /**
   * 任务就是通过 vdom 对象构建 fiber 对象
   * 任务队列是一个数组
   */
  taskQueue.push({
    dom,
    props: {
      children: element
    }
  })
  /**
   * 指定在浏览器空闲时执行任务
   */
  requestIdleCallback(performtask)
}
